/*
 io.cpp
 Matthew Jee
 mcjee@ucsc.edu
*/

#include "io.h"
#include "debug.h"
#include <map>
#include <cstdlib>

std::vector<mj::Path> readTraclusPaths(std::istream &stream) {
    std::string buffer;
    std::vector<std::vector<std::string> > lines;

    while (stream.good()) {
        std::getline(stream, buffer);
        std::istringstream lineStream(buffer);
        std::vector<std::string> line;
        while (lineStream.good()) {
            std::string token;
            lineStream >> token;
            if (token.length() > 0) {
                line.push_back(token);
            }
        }
        if (line.size() > 0) {
            lines.push_back(line);
        }
    }
    
    // if line 1 and line 2 have one element, then they are dimension and count
    // if just line 1 has one element, it is the count
    // if no line has 1 element, then there is neither dimension or count

    std::vector<std::vector<std::string> >::iterator i = lines.begin();
    if (lines[0].size() == 1) {
        if (lines[1].size() == 1) {
            // "read" dimension
            DEBUG_OUT("Reading dimension " << std::endl);
            ++i;
        }

        //"read" count
        DEBUG_OUT("Reading count " << std::endl);
        ++i;
    }

    // infer dimensionality
    std::vector<std::string> firstLine = *i;
    int pointCount = std::stoi(firstLine[1]);
    int dimension = (firstLine.size() - 2) / pointCount;
    double components[3];

    // read the paths
    std::vector<mj::Path> paths;
    for (; i != lines.end(); ++i) {
        std::vector<std::string> line = *i;

        // get trajectory ID
        int trajectoryID = std::stoi(line[0]);
        DEBUG_OUT("Trajectory ID: " << trajectoryID << std::endl);

        // get trajectory point count
        int pointCount = std::stoi(line[1]);
        DEBUG_OUT("Point Count: " << pointCount << std::endl);

        // get the points
        paths.push_back(mj::Path());
        for (int i = 0; i < pointCount; ++i) {
            // read in @dimension components
            for (int j = 0; j < dimension; ++j) {
                components[j] = stod(line[2 + (i * dimension) + j]);
            }

            // assign components to x, y, and z
            // this system only allows up to three dimensional data
            double x = components[0];
            double y = dimension > 1 ? components[1] : 0;
            double z = dimension > 2 ? components[2] : 0;

            mj::Point point(mj::Vector3(x, y, z), i);
            point.pathID = trajectoryID;
            paths[paths.size()-1].push_back(point);
        }
    }

    return paths;
}

// cluster format is the same as trajectory format, but each
// line corresponds to a cluster rather than a trajectory
std::vector<mj::Cluster> readClusters(std::istream &stream) {
    std::string buffer;

    // read dimension
    std::getline(stream, buffer);
    int dimension = std::atoi(buffer.c_str());
    DEBUG_OUT("Dimension: " << dimension << std::endl);

    // read cluster count
    std::getline(stream, buffer);
    size_t clusterCount = std::atol(buffer.c_str());
    DEBUG_OUT("Cluster Count: " << clusterCount << std::endl);

    // read clusters
    double *components = new double[dimension];
    std::vector<mj::Cluster> clusters;
    while (stream.good()) {

        // read cluster ID
        int clusterID;
        stream >> clusterID;
        DEBUG_OUT("Cluster ID: " << clusterID << std::endl);

        // read cluster point count
        int pointCount;
        stream >> pointCount;
        DEBUG_OUT("Point Count: " << pointCount << std::endl);

        // read in the points
        mj::Cluster cluster;
        cluster.tag = clusterID;
        clusters.push_back(cluster);
        for (int i = 0; i < pointCount; ++i) {
            // read in @dimension components
            for (int j = 0; j < dimension; ++j) {
                stream >> components[j];
            }

            // assign components to x, y, and z
            // this system only allows up to three dimensional data
            double x = components[0];
            double y = dimension > 1 ? components[1] : 0;
            double z = dimension > 2 ? components[2] : 0;

            mj::Point point(mj::Vector3(x, y, z), i);
            point.clusterID = clusterID;

            clusters[clusters.size()-1].points.push_back(point);
        }
    }

    delete components;
    return clusters;
}

void writeClusters(std::ostream &stream, std::vector<mj::Cluster> clusters) {

    // write dimension
    int dimension = 2; // hardcoded to 2 for now
    stream << dimension << std::endl;

    // write cluster count
    size_t clusterCount = clusters.size();
    stream << clusterCount << std::endl;

    std::vector<mj::Cluster>::iterator i;
    for (i = clusters.begin(); i != clusters.end(); ++i) {
        mj::Cluster &cluster = *i;
        // write clusterID and point count
        stream << (i - clusters.begin()) << " " << cluster.points.size();

        // write points
        std::vector<mj::Point>::iterator j;
        for (j = cluster.points.begin(); j != cluster.points.end(); ++j) {
            mj::Point &point = *j;
            stream << " " << point.position.x << " " << point.position.y;
        }

        stream << std::endl;
    }
}
